//====================================================//
//========Eric Muehlberg==============================//
//====================================================//
//========Phoenix Forge===============================//
//====================================================//

#include "SDL.h"
#include "SDL_image.h"
#include "SDL_ttf.h"
#include "Camera.h"
#include "Sprite.h"
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <list>
#include "math.h"
#include "engine.h"
#include "GameMap.h"
#include "Console.h"
#include "itoa.h"

engine e = engine();
Console c = Console();
bool grid, lockgrid;
int gridw, gridh;
std::list<MapObject> otypes;
std::list<MapObject>::iterator sobject;


/*
 * Handle controls in Console state
 */
void Console(GameMap *m)
{
	while(SDL_PollEvent(&e.event))
	{
		if(e.event.type == SDL_QUIT)
			e.quit = true;

		if(e.event.type == SDL_KEYDOWN)
		{

			//Handle text input

			if(e.event.key.keysym.unicode == (Uint16)' ' || e.event.key.keysym.unicode == (Uint16)'-')
			{
				c.command += (char)e.event.key.keysym.unicode;
			}
			else if(e.event.key.keysym.unicode >= (Uint16)'0' && e.event.key.keysym.unicode <= (Uint16)'9')
			{
				c.command += (char)e.event.key.keysym.unicode;
			}
			else if(e.event.key.keysym.unicode >= (Uint16)'a' && e.event.key.keysym.unicode <= (Uint16)'z')
			{
				c.command += (char)e.event.key.keysym.unicode;
			}
			else if(e.event.key.keysym.unicode >= (Uint16)'A' && e.event.key.keysym.unicode <= (Uint16)'Z')
			{
				c.command += (char)e.event.key.keysym.unicode;
			}

			switch(e.event.key.keysym.sym)
			{
			case SDLK_RETURN:
				c.Issue(m, &e, &gridw, &gridh);
				break;
			case SDLK_TAB:
				SDL_EnableUNICODE(SDL_DISABLE);
				e.setState(EDITOR);
				break;
			case SDLK_BACKSPACE:
				if(c.command.length() != 0)
					c.command.erase(c.command.length()-1);
				break;
			}

		}
	}
}

/*
 * Handle controls in Editor state
 */
void Editor(GameMap *m)
{
	int mousex, mousey;
	while(SDL_PollEvent(&e.event))
	{
		if(e.event.type == SDL_QUIT)
			e.quit = true;

		if(e.event.type == SDL_KEYDOWN)
		{
			switch(e.event.key.keysym.sym)
			{
			case SDLK_g:
				grid = !grid;
				break;
			case SDLK_d:
				lockgrid = !lockgrid;
				break;
			case SDLK_q:
				sobject->fliphori = !sobject->fliphori;
				sobject->s.sprite = e.FlipImage(sobject->s.sprite, FLIP_HORIZONTAL);
				break;
			case SDLK_w:
				sobject->fliphori = !sobject->flipvert;
				sobject->s.sprite = e.FlipImage(sobject->s.sprite, FLIP_VERTICAL);
				break;
			case SDLK_TAB:
				SDL_EnableUNICODE(SDL_ENABLE);
				e.setState(CONSOLE);
				break;
			}
		}

		Uint8 mousestate = SDL_GetMouseState(&mousex, &mousey);
		e.updaterelative(mousex, mousey);
		e.updatexy(mousex, mousey);

		if(mousestate & SDL_BUTTON(1))
		{
			//NOTE: Minor issue with collision detection when not locked to grid
			//    -> look into this later
			if(sobject != otypes.end())
			{
				int placex, placey;
				int xrel, yrel;
				e.getxyrel(&xrel, &yrel);
				placex = (xrel) - (sobject->s.w)/2;
				placey = (yrel) - (sobject->s.h)/2;
				if(lockgrid)
				{
					placex = floor((xrel)/gridw);
					placey = floor((yrel)/gridh);

					placex = placex * gridw;
					placey = placey * gridh;
					if(!(*m).collision(placex, placey, sobject->s.w, sobject->s.h, &e))
					{
						(*m).placeobject(placex, placey, e.elayer, sobject->s.w, sobject->s.h, sobject->s.sprite, "", sobject->flipvert, sobject->fliphori);
					}
				}
				else
				{
					if(!(*m).collision(placex, placey, sobject->s.w, sobject->s.h, &e))
					{
						(*m).placeobject(placex, placey, e.elayer, sobject->s.w, sobject->s.h, sobject->s.sprite, "", sobject->flipvert, sobject->fliphori);
					}
				}
			}
		}
		if(mousestate & SDL_BUTTON(2))
		{
			if(e.event.type == SDL_MOUSEMOTION)
			{
				e.camupdate(e.mousex - e.lmx, e.mousey - e.lmy);
			}
		}
		if(mousestate & SDL_BUTTON(3))
		{
			//Delete item colliding with mouse
			std::list<MapObject*>::iterator erasetarget = (*m).collisione(mousex+e.c.CameraRect.x, mousey+e.c.CameraRect.y, &e);
			(*m).eraseobject(erasetarget);
		}

		Uint8 *keystate = SDL_GetKeyState(NULL);

		if(keystate[SDLK_SPACE])
		{
			//Jump camera back to the center of the map
			e.c.reset(m->w/2, m->h/2);
		}
		if(keystate[SDLK_e])
		{
			//Next object
			if(sobject == otypes.end())
			{
				sobject = otypes.begin();
			}
			else
				sobject++;

			if(sobject == otypes.end())
				sobject = otypes.begin();
		}
		if(keystate[SDLK_r])
		{
			//previous object
			if(sobject == otypes.begin())
			{
				sobject = otypes.end();
				sobject--;
			}
			else
				sobject--;
		}
		if(keystate[SDLK_1])
		{
			if(e.elayer > 0)
				e.elayer--;
		}
		if(keystate[SDLK_2])
		{
			if(e.elayer < 20)
				e.elayer++;
		}
	}
}

void displayConsole()
{
	SDL_Rect con = SDL_Rect();
	con.x = 0;
	con.y = 0;
	con.h = 800;
	con.w = 800;
	SDL_FillRect(e.screen, &con, SDL_MapRGB(e.screen->format, 0xFF, 0xFF, 0xFF));
	con.h = 1;
	con.y = 780;
	SDL_FillRect(e.screen, &con, SDL_MapRGB(e.screen->format, 0x00, 0x00, 0x00));

	e.printS(e.screen, 0, 780, c.command);
	std::list<std::string>::iterator l = c.contents.begin();
	int y = 0;
	while(l != c.contents.end())
	{
		e.printS(e.screen, 0, y, (*l));
		y += 20;
		l++;
	}
}

void loadobjectdefs(std::string filename)
{
	std::ifstream file;
	file.open(filename.c_str());
	if(file == NULL)
	{
		file.close();
		e.quit = true;
	}

	//Object Attributes
	int objw, objh;
	objw = objh = 0;
	std::string otype, image, ow, oh;

	while(!file.eof())
	{
		file >> otype >> ow >> oh >> image;
		objw = atoi(ow.c_str());
		objh = atoi(oh.c_str());
		otypes.push_back(MapObject(0, 0, 0, objw, objh, e.load_image(image), otype, false, false));
	}

	file.close();
}

int main(int argc, char* args[])
{
	if(e.Init() == false)
		return 1;

	grid = false;
	lockgrid = false;
	gridw = 32;
	gridh = 32;
	GameMap m = GameMap();
	m.h = m.w = 800;

	otypes = std::list<MapObject>();
	loadobjectdefs("objects.def");
	sobject = otypes.begin();
	e.setFont("arial.ttf", 16);

	while(e.quit == false)
	{
		if(e.state == EDITOR)
		{
			Editor(&m);
		}
		else if(e.state == MAPTEST)
		{
			//Maptest();
		}
		else if(e.state == CONSOLE)
		{
			Console(&m);
		}

		SDL_FillRect(e.screen, &e.screen->clip_rect, SDL_MapRGB(e.screen->format, 0xFF, 0xFF, 0xFF));

		if(e.background != NULL)
			e.apply_surface(0, 0, e.background, e.screen, NULL);

		if(grid)
		{
			int vlen = m.h;
			int hlen = m.w;
			SDL_Rect line = SDL_Rect();
			line.x = line.y = line.w = line.h = 0;

			for(int gx = 0; gx <= m.w;)
			{
				line.x = gx;
				line.y = 0;
				line.h = vlen;
				line.w = 1;
				if(e.CollisionCheck(line, e.c.CameraRect))
				{
					line.x -= e.c.CameraRect.x;
					line.y -= e.c.CameraRect.y;
					SDL_FillRect(e.screen, &line, SDL_MapRGB(e.screen->format, 0x00, 0x00, 0x00));
				}
				gx += gridw;
			}
			for(int gy = 0; gy <= m.h;)
			{
				line.x = 0;
				line.y = gy;
				line.h = 1;
				line.w = hlen;
				if(e.CollisionCheck(line, e.c.CameraRect))
				{
					line.x -= e.c.CameraRect.x;
					line.y -= e.c.CameraRect.y;
					SDL_FillRect(e.screen, &line, SDL_MapRGB(e.screen->format, 0x77, 0x77, 0x77));
				}
				gy += gridh;
			}
		}

		auto ldit = e.layers.begin();

		//Display layers
		/*
		 * Steps to solve:
		 * 1: Hash the corners of the camera and find all chunks the camera is in
		 * 2: Run through the chunks one layer at a time and display
		 *
		 * RULE: Camera size must match chunk size or be smaller than chunk size
		 * This allows for a more simplistic display algorithm
		 */
		/*std::list<MapObject*>::iterator mit;
		std::list<MapChunk*>::iterator cit;
		for(int layer = 0; layer < 20; layer++)
		{
			Vect tlk, blk, trk, brk;
			tlk = m.chunkhash(e.c.getx(), e.c.gety(), layer);
			blk = m.chunkhash(e.c.getx(), e.c.gety(), layer);
			trk = m.chunkhash(e.c.getx(), e.c.gety(), layer);
			brk = m.chunkhash(e.c.getx(), e.c.gety(), layer);

			cit = m.getChunk(tlk);
			mit = (*cit)->getContents();
			while((*mit) != NULL)
			{
				if(e.CollisionCheck(e.c.CameraRect, (*mit)->s.CollBox))
				{
					e.apply_surface((*mit)->s.x, (*mit)->s.y, (*mit)->s.sprite, e.screen, NULL);
				}
				mit++;
			}
		}*/
		//Display HUD elements

		e.printS(e.screen, 390, 0, itoa(e.elayer));

		/*for(; iter != m.objects.end(); iter++)
		{
			if(e.CollisionCheck((*iter)->s.CollBox, e.c.CameraRect))
				e.apply_surface((*iter)->s.x - e.c.CameraRect.x, (*iter)->s.y - e.c.CameraRect.y, (*iter)->s.sprite, e.screen, NULL);
		}*/

		if(sobject != otypes.end() && sobject->s.sprite != NULL && e.getState() == EDITOR)
			e.apply_surface(e.mousex-((sobject->s.w)/2), e.mousey-((sobject->s.h)/2), sobject->s.sprite, e.screen, NULL);

		if(e.getState() == CONSOLE)
			displayConsole();

		if(e.screen != NULL)
			SDL_Flip(e.screen);
	}

	SDL_Quit();

	return 0;
}
